#include <os/driver.h>
#include <os/memcache.h>
#include <os/initcall.h>
#include <os/debug.h>
#include <driver/vbe.h>
#include <lib/type.h>
#include <sys/ioctl.h>

static iostatus_t VbeEnter(driver_object_t *driver)
{
    iostatus_t status = IO_SUCCESS;
    device_extension_t *extension;
    device_object_t *devobj;

    status = IoCreateDevice(driver, sizeof(device_extension_t), DEVICE_NAME, DEVICE_TYPE_VIDEO, &devobj);
    if (status != IO_SUCCESS)
    {
        KPrint("VbeEnter: create device failed!\n");
        return status;
    }
    devobj->flags = 0;
    extension = (device_extension_t *)devobj->device_extension;
    extension->device = devobj;
    extension->vbe_info = VBE_INFO_BASE;
    extension->mode_info = VBE_MODE_INFO_BASE;

    if (extension->vbe_info->VbeVersion < 0x200)
    {
        IoDeleteDevice(devobj);
        KPrint(PRINT_ERR "vbe: version %x no supported!\n", extension->vbe_info->VbeVersion);
        status = IO_FAILED;
        return status;
    }

    KPrint("[vbe] xres: %d yres: %d bps:%d pybase %X\n", extension->mode_info->XResolution, extension->mode_info->YResolution, extension->mode_info->BitsPerPixel, extension->mode_info->phyBasePtr);

    extension->virbase = NULL;
    return status;
}

static iostatus_t VbeDevCtl(device_object_t *devobj, io_request_t *ioreq)
{
    device_extension_t *extension = devobj->device_extension;
    uint32_t cmd = ioreq->parame.devctl.code;
    uint32_t arg = ioreq->parame.devctl.arg;
    iostatus_t status;
    video_info_t *video;

    switch (cmd)
    {
    case VIDEOIO_GETINFO:
        video = (video_info_t *)arg;
        if (video)
        {
            video->bits_per_pixel = extension->mode_info->BitsPerPixel;
            video->bytes_per_scan_line = extension->mode_info->BytesPerScanline;
            video->x_res = extension->mode_info->XResolution;
            video->y_res = extension->mode_info->YResolution;
            status = IO_SUCCESS;
            break;
        }
        status = IO_FAILED;
        break;
    default:
        break;
    }
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t VbeExit(driver_object_t *driver)
{
    device_object_t *devobj, *next;
    list_traversal_all_owner_to_next_safe(devobj, next, &driver->device_list, list)
    {
        IoDeleteDevice(devobj);
    }
    string_del(&driver->name);
    return IO_SUCCESS;
}

static iostatus_t VbeOpen(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t VbeClose(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t VbeMMap(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_FAILED;

    ioreq->io_status.info = 0;
    if (ioreq->parame.mmap.len <= extension->mode_info->BytesPerScanline * extension->mode_info->YResolution)
    {
        ioreq->io_status.info = (uint32_t)extension->mode_info->phyBasePtr;
        status = IO_SUCCESS;
    }
    ioreq->io_status.status = status;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t VbeDriverFunc(driver_object_t *driver)
{
    driver->driver_enter = VbeEnter;
    driver->driver_exit = VbeExit;

    driver->dispatch_fun[IOREQ_DEVCTL] = VbeDevCtl;
    driver->dispatch_fun[IOREQ_OPEN] = VbeOpen;
    driver->dispatch_fun[IOREQ_CLOSE] = VbeClose;
    driver->dispatch_fun[IOREQ_MMAP] = VbeMMap;

    string_new(&driver->name, DRIVER_NAME, DRIVER_NAME_LEN);
    return IO_SUCCESS;
}

static void VbeDriverEntry()
{
    KPrint("[driver] create vbe driver\n");
    if (DriverObjectCreate(VbeDriverFunc))
    {
        KPrint(PRINT_ERR "[driver] vbe driver init failed!\n");
    }
}

driver_initcall(VbeDriverEntry);
